//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include "tlsmanager.h"
#include "helper.h"
#include "cthread.h"
#include <aura.h>

void CTlsZone::GetValue(
    /* [in] */ Int32 index,
    /* [out] */ PVoid * ppValue)
{
    assert(NULL != ppValue);
    assert(index >= 0);
    assert(index < TLS_TOTAL_SLOTS);

    *ppValue = m_tlsValue[index];
}

void CTlsZone::SetValue(
    /* [in] */ Int32 index,
    /* [in] */ PVoid pValue)
{
    assert(index >= 0);
    assert(index < TLS_TOTAL_SLOTS);

    m_tlsValue[index] = pValue;
}

CTlsManager::CTlsManager()
{
    for (Int32 i = 0; i < TLS_TOTAL_SLOTS; i++) {
        m_slotType[i] = ThreadLocalSlotType_Free;
    }
    m_slotType[TL_SEQUENCED_SLOT]    = ThreadLocalSlotType_Value;
    m_slotType[TL_QUIT_SLOT]         = ThreadLocalSlotType_Obj;
    m_slotType[TL_DOMAIN_SLOT]       = ThreadLocalSlotType_Value;
    m_slotType[TL_CALLBACK_SLOT]     = ThreadLocalSlotType_Obj;
    m_slotType[TL_HELPER_INFO_SLOT]  = ThreadLocalSlotType_Value;
    m_slotType[TL_APPLET_SLOT]       = ThreadLocalSlotType_Obj;
    m_slotType[TL_ORG_CALLBACK_SLOT] = ThreadLocalSlotType_Obj;
    m_slotType[TL_APPLET_DIR_SLOT]   = ThreadLocalSlotType_Value;
}

CTlsManager::~CTlsManager()
{
    if (NULL != m_hLock) {
        AuraCriticalSection_Destroy(m_hLock);
        m_hLock = NULL;
    }
    assert(m_tlsZoneList.IsEmpty());
}

ECode CTlsManager::Constructor()
{
    m_tlsZoneList.Initialize();
    return AuraCriticalSection_Create(&m_hLock);
}

ECode CTlsManager::AcquireSlot(
    /* [in] */ ThreadLocalSlotType type,
    /* [out] */ Int32 * pSlotIndex)
{
    Int32 index = 0;

    if ((type <= ThreadLocalSlotType_Free)
        || (type > ThreadLocalSlotType_Obj)
        || (NULL == pSlotIndex)) {
        return E_INVALID_ARGUMENT;
    }

    LOCK;
    for (; index < TLS_MINIMUM_AVAILABLE; index++) {
        if (ThreadLocalSlotType_Free == m_slotType[index]) {
            m_slotType[index] = type;
            *pSlotIndex = index;
            break;
        }
    }
    UNLOCK;

    if (index >= TLS_MINIMUM_AVAILABLE) {
        return E_OUT_OF_MEMORY;
    }
    return NOERROR;
}

ECode CTlsManager::RelinquishSlot(
    /* [in] */ Int32 slotIndex,
    /* [out] */ BufferOf<Int32> * pUsedPtrs)
{
    CTlsZone * pTlsZone = NULL;
    Int32 i = 0;

    if ((slotIndex <0)
        || (slotIndex >= TLS_MINIMUM_AVAILABLE)
        || (NULL == pUsedPtrs)) {
        return E_INVALID_ARGUMENT;
    }

    LOCK;
    if (ThreadLocalSlotType_Free == m_slotType[slotIndex]) {
        UNLOCK;
        return E_INVALID_ARGUMENT;
    }
    assert(m_slotType[slotIndex] > ThreadLocalSlotType_Free);
    assert(m_slotType[slotIndex] <= ThreadLocalSlotType_Obj);

    m_slotType[slotIndex] = ThreadLocalSlotType_Free;

    pUsedPtrs->SetUsed(0);
    ForEachDLinkNode(CTlsZone*, pTlsZone, &m_tlsZoneList)
    {
        if (ThreadLocalSlotType_Value != m_slotType[slotIndex]) {
            pUsedPtrs->SetUsed(i+1);
            pTlsZone->GetValue(slotIndex, (PVoid *)&(*pUsedPtrs)[i]);
            i++;
        }
        pTlsZone->SetValue(slotIndex, NULL);
    }
    UNLOCK;

    return NOERROR;
}

ECode CTlsManager::CleanupSlot(
    /* [in] */ Int32 slotIndex,
    /* [out] */ BufferOf<Int32> * pUsedPtrs)
{
    CTlsZone * pTlsZone = NULL;
    Int32 i = 0;

    if ((slotIndex <0)
        || (slotIndex >= TLS_TOTAL_SLOTS)
        || (NULL == pUsedPtrs)) {
        return E_INVALID_ARGUMENT;
    }

    LOCK;
    if (ThreadLocalSlotType_Free == m_slotType[slotIndex]) {
        UNLOCK;
        return E_INVALID_ARGUMENT;
    }
    assert(m_slotType[slotIndex] > ThreadLocalSlotType_Free);
    assert(m_slotType[slotIndex] <= ThreadLocalSlotType_Obj);

    pUsedPtrs->SetUsed(0);
    ForEachDLinkNode(CTlsZone*, pTlsZone, &m_tlsZoneList)
    {
        if (ThreadLocalSlotType_Value != m_slotType[slotIndex]) {
            pUsedPtrs->SetUsed(i+1);
            pTlsZone->GetValue(slotIndex, (PVoid *)&(*pUsedPtrs)[i]);
            i++;
        }
        pTlsZone->SetValue(slotIndex, NULL);
    }
    UNLOCK;

    return NOERROR;
}


ECode CTlsManager::GetSlotValue(
    /* [in] */ ThreadLocalSlotType type,
    /* [in] */ Int32 slotIndex,
    /* [out] */ PVoid * ppValue)
{
    if ((type <= ThreadLocalSlotType_Free)
        || (type > ThreadLocalSlotType_Obj)
        || (slotIndex < 0)
        || (slotIndex >= TLS_TOTAL_SLOTS)) {
        return E_INVALID_ARGUMENT;
    }
    LOCK;
    if (type != m_slotType[slotIndex]) {
        UNLOCK;
        return E_INVALID_ARGUMENT;
    }
    Helper_TlsZone_GetCurrent()->GetValue(slotIndex, ppValue);
    UNLOCK;

    return NOERROR;
}

ECode CTlsManager::SetSlotValue(
    /* [in] */ ThreadLocalSlotType type,
    /* [in] */ Int32 slotIndex,
    /* [in] */ PVoid pValue)
{
    if ((type <= ThreadLocalSlotType_Free)
        || (type > ThreadLocalSlotType_Obj)
        || (slotIndex < 0)
        || (slotIndex >= TLS_TOTAL_SLOTS)) {
        return E_INVALID_ARGUMENT;
    }

    LOCK;
    if (type != m_slotType[slotIndex]) {
        UNLOCK;
        return E_INVALID_ARGUMENT;
    }
    Helper_TlsZone_GetCurrent()->SetValue(slotIndex, pValue);
    UNLOCK;

    return NOERROR;
}

void CTlsManager::AttachTlsZone(
    /* [in] */ CTlsZone * pTlsZone)
{
    LOCK;
    m_tlsZoneList.InsertLast(pTlsZone);
    UNLOCK;
}

void CTlsManager::DetachTlsZone(
    /* [in] */ CTlsZone * pTlsZone)
{
    LOCK;
    pTlsZone->Detach();
    UNLOCK;
}

